#ifndef SMART_ASSERT_HEADER_FROM_CORE_INCLUDED
#define SMART_ASSERT_HEADER_FROM_CORE_INCLUDED
////////////////////////////////////////////////////////////////////////////////
//	Copyright (c) 2004 by Michael 'Astar' Sotnikov
//	stari4ek at gmail dot com
////////////////////////////////////////////////////////////////////////////////

// SMART_ASSERT - redesign of ideas, described by Andrei Alexandrescu and
// John Torjo in.
// http://www.cuj.com/documents/s=8464/cujcexp0308alexandr/cujcexp0308alexandr.html

#include <string>
#include <vector>
#include <exception>

#include <boost/lexical_cast.hpp>
#include <boost/current_function.hpp> 

#include "cStreamHelper.hpp"

////////////////////////////////////////////////////////////////////////////////
enum AssertLevel_e{
	LVL_WARN,						// by default: just logging
	LVL_ERROR,						//             throw exception
	LVL_FATAL,						//             full logging and aborting
	LVL_TOTAL  
};

////////////////////////////////////////////////////////////////////////////////
class AssertContext_c{
public:
	AssertContext_c();

	void					setLevel( const AssertLevel_e );
	AssertLevel_e			getLevel() const;
	
	void					setFile( const char* );
	void					setFile( const std::string& );
	const std::string&		getFile() const;

	void					setFunc( const char* );
	void					setFunc( const std::string& );
	const std::string&		getFunc() const;

	void					setLine( unsigned int );
	unsigned int 			getLine() const;

	void					setDesc( const char* );
	void					setDesc( const std::string& );
	const std::string&		getDesc() const;

	void					setExpr( const char* );
	void					setExpr( const std::string& );
	const std::string&		getExpr() const;
	
	typedef std::pair<std::string, std::string>	ValueAndString_t;
	typedef std::vector<ValueAndString_t>		ValsArray_t;
	
	void					addValue( const char*, const std::string& value );
	void					addValue( const std::string&, const std::string& value );
	const ValsArray_t&		getValues() const;
	
	// return user-friendly assert message
	const std::string		getMsg() const;

protected:
	AssertLevel_e			m_level;
	std::string 			m_file;
	std::string 			m_func;
	unsigned int 			m_line;
	std::string 			m_desc;
	std::string 			m_expr;
	ValsArray_t				m_values;	
};

////////////////////////////////////////////////////////////////////////////////
// as usual exception, but keep context of assertion 
struct AssertException_s : public std::exception {
						
							AssertException_s( const AssertContext_c& );
	virtual 				~AssertException_s() throw();
	
	virtual const char* 	what() const throw();
protected:
	AssertContext_c			m_context;
};

////////////////////////////////////////////////////////////////////////////////
// Helper for unrolling complex SMART_ASSERT macroses
class SmartAssert_c
{
public:
	SmartAssert_c( const char* expr ):
		SMART_ASSERT_A( *this ),SMART_ASSERT_B( *this )
	{
		m_context.setExpr( expr );
	}

	~SmartAssert_c();
	
	SmartAssert_c& 	setContext( const char* file, const char* func, const unsigned int line );
	SmartAssert_c& 	setExpr( const char* );
	
	template<typename Value_t>
	SmartAssert_c& 	addValue( const char* name, const Value_t& value ){
		try{
			m_context.addValue( name, boost::lexical_cast<std::string>( value ) );
		} catch( const boost::bad_lexical_cast& )
		{
			m_context.addValue( name, "value extraction failed." );
		}

		return ( *this );
	}
	
	SmartAssert_c& 	warn( const char* );
	SmartAssert_c& 	warn( const std::string& );
	SmartAssert_c& 	error( const char* );
	SmartAssert_c& 	error( const std::string& );
	SmartAssert_c& 	fatal( const char* );
	SmartAssert_c& 	fatal( const std::string& );
	
	// NULL as callback will disable handling specified level of assertions  
	typedef void (*HandlerCallback_t)( const AssertContext_c& ); 
	static void		setHandler(	AssertLevel_e, HandlerCallback_t );
		
private:
	static void 	defaultWarnHandler( const AssertContext_c& );
	static void 	defaultErrorHandler( const AssertContext_c& );
	static void 	defaultFatalHandler( const AssertContext_c& ); 
	
public:
	SmartAssert_c&	SMART_ASSERT_A;
	SmartAssert_c&	SMART_ASSERT_B;
	
private:
	AssertContext_c 			m_context;
	static HandlerCallback_t	m_callbacks[LVL_TOTAL];
private:
	// forbidden
					SmartAssert_c( const SmartAssert_c& );
	SmartAssert_c&	operator=( const SmartAssert_c& );
	SmartAssert_c*	operator*( const SmartAssert_c& );

}; // class Assert_c

////////////////////////////////////////////////////////////////////////////////
// These macroses will help us to unfold complex assertions, like
// SMART_ASSERT( a >= 0 )( a ).warn( "A must be >=0" );
#define SMART_ASSERT_A(x) SMART_ASSERT_OP(x, B)
#define SMART_ASSERT_B(x) SMART_ASSERT_OP(x, A)

#define SMART_ASSERT_OP(x, next) \
    SMART_ASSERT_A.addValue( #x, (x) ).SMART_ASSERT_ ## next

////////////////////////////////////////////////////////////////////////////////   
#ifndef NDEBUG
#define SMART_ASSERT(expr) \
			if ( expr ) \
				{ ; } \
			else \
				SmartAssert_c( #expr ).setContext( __FILE__, BOOST_CURRENT_FUNCTION, __LINE__ ).SMART_ASSERT_A

#define SMART_VERIFY(expr) \
			if ( expr ) \
				{ ; } \
			else \
	    		SmartAssert_c( #expr ).setContext( __FILE__, BOOST_CURRENT_FUNCTION, __LINE__ ).SMART_ASSERT_A

#define SMART_FAILED() \
				SmartAssert_c( "" ).setContext( __FILE__, BOOST_CURRENT_FUNCTION, __LINE__ ).SMART_ASSERT_A
			
#else
#define SMART_ASSERT(expr) \
	    if ( true )
	    	{;} \
	    else \
	    	SmartAssert_c("").SMART_ASSERT_A

#define SMART_VERIFY(expr) \
			if ( true ) \
				{ (void)(expr); } \
			else \
	    		SmartAssert_c("").SMART_ASSERT_A
	    	
#define SMART_FAILED() \
			if ( true ) \
				{ ; } \
			else \
				SmartAssert_c( "" ).SMART_ASSERT_A

#endif // ifndef NDEBUG

#endif /* SMART_ASSERT_HEADER_FROM_CORE_INCLUDED */

